package pobidz;

import java.awt.Color;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;


public class Filters {

	private Picture picture;

	private int imageWidth;
	private int imageHeight;
	private int[][] obr;
	private boolean isFFT;
	
	public Filters(Picture picture) {
		super();
		this.picture = picture;
		this.imageWidth = picture.getOriginalImage().getWidth();
		this.imageHeight = picture.getOriginalImage().getHeight();
	}

	public Filters() {
		//this.imageWidth = picture.getOriginalImage().getWidth();
		//this.imageHeight = picture.getOriginalImage().getHeight();
	}

	public void avarangeFilter(int frameWidth, int frameHeight) {
		int n = frameWidth / 2;
		int m = frameHeight / 2;

		for (int x = 0; x < this.imageWidth; x++) {
			for (int y = 0; y < this.imageHeight; y++) {
				int littleXMin = 0;
				int littleYMin = 0;
				int littleXMax = 0;
				int littleYMax = 0;
				littleXMin = ((x - n) >= 0) ? (x - n) : 0;
				littleYMin = ((y - m) >= 0) ? (y - m) : 0;
				littleXMax = ((x + n) < this.imageWidth) ? (x + n + 1) : this.imageWidth;
				littleYMax = ((y + m) < this.imageHeight) ? (y + m + 1) : this.imageHeight;
				int redValue = 0;
				int greenValue = 0;
				int blueValue = 0;
				int number = 0;
				int rgb = 0;
				for (int littleX = littleXMin; littleX < littleXMax; littleX++) {
					for (int littleY = littleYMin; littleY < littleYMax; littleY++) {
						number++;
						rgb = this.picture.getOrgin().getRGB(littleX, littleY);
						blueValue += (rgb >> 0) & 0xFF;
						greenValue += (rgb >> 8) & 0xFF;
						redValue += (rgb >> 16) & 0xFF;
					}
				}
				Color rgbColor = new Color(redValue / number, greenValue
						/ number, blueValue / number);
				this.picture.getBi().setRGB(x, y, rgbColor.getRGB());
				this.picture.getOriginalImage().setRGB(x, y, rgbColor.getRGB());
			}
		}
	}

	public void medianFilter(int frameWidth, int frameHeight) {
		int n = frameWidth / 2;
		int m = frameHeight / 2;

		for (int x = 0; x < this.imageWidth; x++) {
			for (int y = 0; y < this.imageHeight; y++) {
				int littleXMin = 0;
				int littleYMin = 0;
				int littleXMax = 0;
				int littleYMax = 0;
				littleXMin = ((x - n) >= 0) ? (x - n) : 0;
				littleYMin = ((y - m) >= 0) ? (y - m) : 0;
				littleXMax = ((x + n) < this.imageWidth) ? (x + n + 1) : this.imageWidth;
				littleYMax = ((y + m) < this.imageHeight) ? (y + m + 1) : this.imageHeight;
				ArrayList<Integer> redValueList = new ArrayList<Integer>();
				ArrayList<Integer> greenValueList = new ArrayList<Integer>();
				ArrayList<Integer> blueValueList = new ArrayList<Integer>();
				int number = 0;
				int rgb = 0;
				for (int littleX = littleXMin; littleX < littleXMax; littleX++) {
					for (int littleY = littleYMin; littleY < littleYMax; littleY++) {
						number++;
						rgb = this.picture.getOriginalImage().getRGB(littleX,
								littleY);
						blueValueList.add(new Integer((rgb >> 0) & 0xFF));
						greenValueList.add(new Integer((rgb >> 8) & 0xFF));
						redValueList.add(new Integer((rgb >> 16) & 0xFF));
					}
				}
				Collections.sort(redValueList);
				Collections.sort(greenValueList);
				Collections.sort(blueValueList);
				if (number % 2 == 1) {
					Color rgbColor = new Color(redValueList.get(number / 2),
							greenValueList.get(number / 2),
							blueValueList.get(number / 2));
					this.picture.getBi().setRGB(x, y, rgbColor.getRGB());
				} else {
					Color rgbColor = new Color(
							(redValueList.get(number / 2) + redValueList
									.get((number / 2) + 1)) / 2,
							(greenValueList.get(number / 2) + greenValueList
									.get((number / 2) + 1)) / 2, (blueValueList
									.get(number / 2) + blueValueList
									.get((number / 2) + 1)) / 2);
					this.picture.getBi().setRGB(x, y, rgbColor.getRGB());
					this.picture.getOriginalImage().setRGB(x, y,
							rgbColor.getRGB());
				}
			}
		}
	}

	public int[][] buildingMatrix(int n, String type) {

		int middle = (n - 1) / 2;
		int[][] matrix = new int[n][n];

		if (type.equals("North")) {

			for (int i = 0; i < n; i++) {
				if (i <= middle) {
					for (int j = 0; j < n; j++) {

						matrix[i][j] = 1;
					}
				} else {
					for (int j = 0; j < n; j++) {
						matrix[i][j] = -1;
					}
				}
			}
			matrix[middle][middle] = -2;

		} else if (type.equals("South-East")) {

			for (int i = 0; i < n; i++) {

				for (int j = 0; j < n; j++) {
					if (i + j < (n - 1)) {
						matrix[i][j] = -1;
					} else {

						matrix[i][j] = 1;
					}
				}

			}
			matrix[middle][middle] = -2;

		} else if (type.equals("East")) {

			for (int i = 0; i < n; i++) {

				for (int j = 0; j < middle; j++) {
					matrix[i][j] = -1;
				}

				for (int j = middle; j < n; j++) {
					matrix[i][j] = 1;
				}

			}
			matrix[middle][middle] = -2;

		} else {

			for (int i = 0; i < n; i++) {

				for (int j = 0; j < n; j++) {
					if (j < i) {
						matrix[i][j] = -1;
					} else {
						matrix[i][j] = 1;
					}

				}
			}
			matrix[middle][middle] = -2;
		}

		return matrix;
	}

	public BufferedImage splot(BufferedImage img, int[][] matrix) {

		
		
		BufferedImage image = img;
		int middle = (matrix.length - 1) / 2;
		List<UnprocessedColor> unpColors = new ArrayList<UnprocessedColor>();

		for (int i = middle; i < image.getHeight() - middle; i++) {
			for (int j = middle; j < image.getWidth() - middle; j++) {

				splotValue(j,i, matrix, image, unpColors);

			}
		}

		Normalization normalization = new Normalization(unpColors);

		return normalization.normalize(img, middle, false);
	}

	private void splotValue(int pixelX, int pixelY, int[][] matrix,
			BufferedImage img, List<UnprocessedColor> unpColors) {

		int newValRed = 0;
		int newValGreen = 0;
		int newValBlue = 0;
		int k = 0;

		int middle = (matrix.length - 1) / 2;

		for (int i = -middle; i <= middle; i++) {
			for (int j = -middle; j <= middle; j++) {
				k += matrix[middle + i][middle + j];

				newValRed += matrix[middle + i][middle + j]
						* new Color(img.getRGB(pixelX + i, pixelY + j))
								.getRed();

				newValGreen += matrix[middle + i][middle + j]
						* new Color(img.getRGB(pixelX + i, pixelY + j))
								.getGreen();

				newValBlue += matrix[middle + i][middle + j]
						* new Color(img.getRGB(pixelX + i, pixelY + j))
								.getBlue();

			}
		}

		if (k == 0) {
			k = 1;
		}

		unpColors.add(new UnprocessedColor(newValRed / k, newValGreen / k,
				newValBlue / k));

	}

	public void robertsCross2() {

		Normalization normal = new Normalization();
		for (int y = 0; y < this.imageHeight - 1; y++) {
			for (int x = 0; x < this.imageWidth - 1; x++) {
				int rgbXY = this.picture.getOriginalImage().getRGB(x, y);
				int rgbX1Y1 = this.picture.getOriginalImage().getRGB(x + 1,
						y + 1);
				int rgbX1Y = this.picture.getOriginalImage().getRGB(x + 1, y);
				int rgbXY1 = this.picture.getOriginalImage().getRGB(x, y + 1);
				int blueValue = 0;
				int greenValue = 0;
				int redValue = 0;

				blueValue = Math.abs(((rgbXY >> 0) & 0xFF)
						- ((rgbX1Y1 >> 0) & 0xFF))
						+ Math.abs(((rgbXY1 >> 0) & 0xFF)
								- ((rgbX1Y >> 0) & 0xFF));

				greenValue = Math.abs(((rgbXY >> 8) & 0xFF)
						- ((rgbX1Y1 >> 8) & 0xFF))
						+ Math.abs(((rgbXY1 >> 8) & 0xFF)
								- ((rgbX1Y >> 8) & 0xFF));

				redValue = Math.abs(((rgbXY >> 16) & 0xFF)
						- ((rgbX1Y1 >> 16) & 0xFF))
						+ Math.abs(((rgbXY1 >> 16) & 0xFF)
								- ((rgbX1Y >> 16) & 0xFF));

				normal.addColor(new UnprocessedColor(redValue, greenValue,
						blueValue));
			}
		}
		this.picture.setBi(normal.normalize(this.picture.getBi(), 0, true));
		this.picture.setOriginalImage(this.picture.getBi());
	}
	
	
	
	public BufferedImage fft(BufferedImage image, boolean isMagnitude, boolean isReverseFFT, ComplexImage complexImage)
	{
		isFFT = true;
		ComplexPoint[][] imgR = new ComplexPoint[image.getWidth()][image.getHeight()];
		ComplexPoint[][] imgG = new ComplexPoint[image.getWidth()][image.getHeight()];
		ComplexPoint[][] imgB = new ComplexPoint[image.getWidth()][image.getHeight()];
		ComplexPoint[][] imgRmodified = new ComplexPoint[image.getWidth()][image.getHeight()];
		ComplexPoint[][] imgGmodified = new ComplexPoint[image.getWidth()][image.getHeight()];
		ComplexPoint[][] imgBmodified = new ComplexPoint[image.getWidth()][image.getHeight()];
		long start = System.nanoTime();
		
		if (!isReverseFFT)
		{
			writeToTable(image, imgR, imgG, imgB);
		}
		else
		{
                   // writeToTable(image, imgR, imgG, imgB);
			writeToTableReverse(image, complexImage, imgR, imgG, imgB);
		}
		
		for (int i=0; i<image.getWidth(); i++)
		{
			for (int j=0; j<image.getHeight(); j++)
			{
				imgR[i][j].setReal(imgR[i][j].getReal()*Math.pow(-1,i+j));
				imgR[i][j].setImaginary(imgR[i][j].getImaginary()*Math.pow(-1,i+j));
				imgG[i][j].setReal(imgG[i][j].getReal()*Math.pow(-1,i+j));
				imgG[i][j].setImaginary(imgG[i][j].getImaginary()*Math.pow(-1,i+j));
				imgB[i][j].setReal(imgB[i][j].getReal()*Math.pow(-1,i+j));
				imgB[i][j].setImaginary(imgB[i][j].getImaginary()*Math.pow(-1,i+j));
			}
		}
		
		
		for (int i=0; i<image.getHeight(); i++)
		{
			//System.out.println("Wiersz " + i);
			imgRmodified[i] = countingFFT(imgR[i], isReverseFFT);
			imgGmodified[i] = countingFFT(imgG[i], isReverseFFT);
			imgBmodified[i] = countingFFT(imgB[i], isReverseFFT);

			
		}

		
		for (int i=0; i<image.getWidth(); i++)
		{
			//System.out.println("Kolumna " + i);
			
			ComplexPoint[] redTemp = countingFFT(toColumn(imgRmodified, i), isReverseFFT);
			
			for (int z=0; z<redTemp.length; z++)
			{
				imgR[z][i] = redTemp[z];
			}
			
			ComplexPoint[] greenTemp = countingFFT(toColumn(imgGmodified, i), isReverseFFT);
			
			for (int z=0; z<greenTemp.length; z++)
			{
				imgG[z][i] = greenTemp[z];
			}
			
			ComplexPoint[] blueTemp = countingFFT(toColumn(imgBmodified, i), isReverseFFT);
			
			for (int z=0; z<blueTemp.length; z++)
			{
				imgB[z][i] = blueTemp[z];
			}

		}

		
		int[][] magnitudeOrPhaseTableR = new int[image.getWidth()][image.getHeight()];
		int[][] magnitudeOrPhaseTableG = new int[image.getWidth()][image.getHeight()];
		int[][] magnitudeOrPhaseTableB = new int[image.getWidth()][image.getHeight()];
		
		if (!isReverseFFT)
		{
			picture.setComplexImage(new ComplexImage(image.getWidth(), image.getHeight(), imgR, imgG, imgB));

		}

		
		
		for (int i=0; i<image.getWidth(); i++)
		{
			for (int j=0; j<image.getHeight(); j++)
			{
				imgR[i][j].setReal(imgR[i][j].getReal()*Math.pow(-1,i+j));
				imgR[i][j].setImaginary(imgR[i][j].getImaginary()*Math.pow(-1,i+j));
				imgG[i][j].setReal(imgG[i][j].getReal()*Math.pow(-1,i+j));
				imgG[i][j].setImaginary(imgG[i][j].getImaginary()*Math.pow(-1,i+j));
				imgB[i][j].setReal(imgB[i][j].getReal()*Math.pow(-1,i+j));
				imgB[i][j].setImaginary(imgB[i][j].getImaginary()*Math.pow(-1,i+j));
			}
		}
		
		//Magnitude albo Phase
		if (isMagnitude && !isReverseFFT)
		{
			for (int i=0; i<magnitudeOrPhaseTableR.length; i++)
			{
				for (int j=0; j<magnitudeOrPhaseTableR[0].length; j++)
				{
					magnitudeOrPhaseTableR[i][j] = (int)imgR[i][j].magnitude();
					magnitudeOrPhaseTableG[i][j] = (int)imgG[i][j].magnitude();
					magnitudeOrPhaseTableB[i][j] = (int)imgB[i][j].magnitude();
				}
			}
		}
		else if (!isMagnitude && !isReverseFFT)
		{
			for (int i=0; i<magnitudeOrPhaseTableR.length; i++)
			{
				for (int j=0; j<magnitudeOrPhaseTableR[0].length; j++)
				{
					magnitudeOrPhaseTableR[i][j] = (int)imgR[i][j].phase();
					magnitudeOrPhaseTableG[i][j] = (int)imgG[i][j].phase();
					magnitudeOrPhaseTableB[i][j] = (int)imgB[i][j].phase();
				}
			}
		}
		else if (isReverseFFT)
		{
			for (int i=0; i<magnitudeOrPhaseTableR.length; i++)
			{
				for (int j=0; j<magnitudeOrPhaseTableR[0].length; j++)
				{
					magnitudeOrPhaseTableR[i][j] = (int)imgR[i][j].getReal();
					magnitudeOrPhaseTableG[i][j] = (int)imgG[i][j].getReal();
					magnitudeOrPhaseTableB[i][j] = (int)imgB[i][j].getReal();
				}
			}
		}
		
		
		
		
		//Normalizacja
		
		List<UnprocessedColor> upc = new ArrayList<UnprocessedColor>();
		

			for (int i=0; i<image.getHeight(); i++)
			{
				for (int j=0; j<image.getWidth(); j++)
				{
					upc.add(new UnprocessedColor(magnitudeOrPhaseTableR[i][j], 
							magnitudeOrPhaseTableG[i][j], magnitudeOrPhaseTableB[i][j]));
				}
			}


		
		if (!isReverseFFT)
		{
			for (int i=0; i<upc.size(); i++)
			{
				upc.get(i).setRed((int)(0.7 * Math.log(1 + upc.get(i).getRed())));
				upc.get(i).setGreen((int)(0.7 * Math.log(1 + upc.get(i).getGreen())));
				upc.get(i).setBlue((int)(0.7 * Math.log(1 + upc.get(i).getBlue())));
			}
		}




		
		Normalization n = new Normalization(upc);
		image = n.normalize(image,0, false);

		
		
		
		long end = System.nanoTime();
		System.out.println((end-start)/1000000000.0);
		
		return image;
	}
	

	
	private ComplexPoint[] toColumn(ComplexPoint[][] tab, int columnNumber)
	{
		ComplexPoint[] column = new ComplexPoint[tab.length];
		
		for (int i=0; i<tab.length; i++)
		{
			column[i] = tab[i][columnNumber];
		}
		
		return column;
	}
	

	
	
	
	private ComplexPoint[] countingFFT(ComplexPoint[] tab, boolean isReverseFFT)
	{
		
		int nbOfLevels = (int)(Math.log(imageHeight)/Math.log(2));

		int[] order = correctOrder(tab.length);
		ComplexPoint[] ordered = new ComplexPoint[order.length];

		for (int i=0; i<tab.length; i++)
		{
			ordered[i] = tab[order[i]];
		}
		
		for (int i=0; i<tab.length; i++)
		{
			tab[i] = ordered[i];
		}
		
		for (int i=0; i<nbOfLevels; i++)
		{
			
			int nested = (int)Math.pow(2,nbOfLevels-1-i);
			
			for (int j=0; j<nested; j++)
			{		
				
				ComplexPoint[] temp = new ComplexPoint[tab.length/nested];
				
			
				int firstIndex = j *(tab.length/nested);
				int counter = 0;
				
				
				for (int k = firstIndex; k<firstIndex + tab.length/nested; k++)
				{
					temp[counter++] = tab[k];
				}

				ComplexPoint[] result = new ComplexPoint[tab.length];
				
				if (!isReverseFFT)
				{
					result = nextLevel(temp, temp.length, j, -1);
				}
				else
				{
					result = nextLevel(temp, temp.length, j, 1);
				}

				counter = 0;
				
				for (int k = firstIndex; k<firstIndex + tab.length/nested; k++)
				{
					tab[k] = result[counter++];
				}
				
			}
			
		}

		return tab;
	}
	
	
	
	
	
	private ComplexPoint[] nextLevel(ComplexPoint[] color, int n, int k, int factor)
	{
		
		int colorLength = color.length;
		
		ComplexPoint[] clr = new ComplexPoint[colorLength];
		
		int middle = colorLength/2;
		
		for (int i=0; i<middle; i++)
		{
			double arg = 2 * Math.PI * i / n;
			double wnk = Math.cos(arg) ;
			double wnk2 = factor * Math.sin(arg);

			
			clr[i] = new ComplexPoint(color[i].getReal() + wnk*color[middle+i].getReal() 
					- wnk2*color[middle+i].getImaginary(),
					color[i].getImaginary() + wnk*color[middle+i].getImaginary()
					+ color[middle+i].getReal()*wnk2);
			
			clr[i+middle] = new ComplexPoint(color[i].getReal() - wnk*color[i+middle].getReal() 
					+ wnk2*color[i+middle].getImaginary(),
					color[i].getImaginary() - wnk*color[i+middle].getImaginary()
					- color[i+middle].getReal()*wnk2);
		}
	
	
		
		
		
		return clr;
	}
	
	public BufferedImage fht2(BufferedImage image, boolean isMagnitude, boolean isReverseFFT, ComplexImage complexImage)
	{
		isFFT = false;
		ComplexPoint[][] imgR = new ComplexPoint[imageWidth][imageHeight];
		ComplexPoint[][] imgG = new ComplexPoint[imageWidth][imageHeight];
		ComplexPoint[][] imgB = new ComplexPoint[imageWidth][imageHeight];
		ComplexPoint[][] imgRmodified = new ComplexPoint[imageWidth][imageHeight];
		ComplexPoint[][] imgGmodified = new ComplexPoint[imageWidth][imageHeight];
		ComplexPoint[][] imgBmodified = new ComplexPoint[imageWidth][imageHeight];
		long start = System.nanoTime();
		
		if (!isReverseFFT)
		{
			writeToTable(image, imgR, imgG, imgB);
		}
		else
		{
			writeToTableReverse(image, complexImage, imgR, imgG, imgB);
		}
		

		for (int i=0; i<imageWidth; i++)
		{
			for (int j=0; j<imageHeight; j++)
			{
				imgR[i][j].setReal(imgR[i][j].getReal()*Math.pow(-1,i+j));
				imgR[i][j].setImaginary(imgR[i][j].getImaginary()*Math.pow(-1,i+j));
				imgG[i][j].setReal(imgG[i][j].getReal()*Math.pow(-1,i+j));
				imgG[i][j].setImaginary(imgG[i][j].getImaginary()*Math.pow(-1,i+j));
				imgB[i][j].setReal(imgB[i][j].getReal()*Math.pow(-1,i+j));
				imgB[i][j].setImaginary(imgB[i][j].getImaginary()*Math.pow(-1,i+j));
			}
		}
		
		for (int i=0; i<imageHeight; i++)
		{
			//System.out.println("Wiersz " + i);
			imgRmodified[i] = countingFHT2(imgR[i], isReverseFFT);
			imgGmodified[i] = countingFHT2(imgG[i], isReverseFFT);
			imgBmodified[i] = countingFHT2(imgB[i], isReverseFFT);

			
		}

		
		for (int i=0; i<imageWidth; i++)
		{
			//System.out.println("Kolumna " + i);
			
			ComplexPoint[] redTemp = countingFHT2(toColumn(imgRmodified, i), isReverseFFT);
			
			for (int z=0; z<redTemp.length; z++)
			{
				imgR[z][i] = redTemp[z];
			}
			
			ComplexPoint[] greenTemp = countingFHT2(toColumn(imgGmodified, i), isReverseFFT);
			
			for (int z=0; z<greenTemp.length; z++)
			{
				imgG[z][i] = greenTemp[z];
			}
			
			ComplexPoint[] blueTemp = countingFHT2(toColumn(imgBmodified, i), isReverseFFT);
			
			for (int z=0; z<blueTemp.length; z++)
			{
				imgB[z][i] = blueTemp[z];
			}
		}

		int[][] magnitudeOrPhaseTableR = new int[imageWidth][imageHeight];
		int[][] magnitudeOrPhaseTableG = new int[imageWidth][imageHeight];
		int[][] magnitudeOrPhaseTableB = new int[imageWidth][imageHeight];
		
		for (int i=0; i<imageWidth; i++)
		{
			for (int j=0; j<imageHeight; j++)
			{
				imgR[i][j].setReal(imgR[i][j].getReal()*Math.pow(-1,i+j));
				imgR[i][j].setImaginary(imgR[i][j].getImaginary()*Math.pow(-1,i+j));
				imgG[i][j].setReal(imgG[i][j].getReal()*Math.pow(-1,i+j));
				imgG[i][j].setImaginary(imgG[i][j].getImaginary()*Math.pow(-1,i+j));
				imgB[i][j].setReal(imgB[i][j].getReal()*Math.pow(-1,i+j));
				imgB[i][j].setImaginary(imgB[i][j].getImaginary()*Math.pow(-1,i+j));
			}
		}
		
		if (!isReverseFFT)
		{
			picture.setComplexImage(new ComplexImage(imageWidth, imageHeight, imgR, imgG, imgB));

		}

		
		if (isMagnitude && !isReverseFFT)
		{
			for (int i=0; i<magnitudeOrPhaseTableR.length; i++)
			{
				for (int j=0; j<magnitudeOrPhaseTableR[0].length; j++)
				{
					magnitudeOrPhaseTableR[i][j] = (int)imgR[i][j].magnitude();
					magnitudeOrPhaseTableG[i][j] = (int)imgG[i][j].magnitude();
					magnitudeOrPhaseTableB[i][j] = (int)imgB[i][j].magnitude();
				}
			}
		}
		else if (!isMagnitude && !isReverseFFT)
		{
			for (int i=0; i<magnitudeOrPhaseTableR.length; i++)
			{
				for (int j=0; j<magnitudeOrPhaseTableR[0].length; j++)
				{
					magnitudeOrPhaseTableR[i][j] = (int)imgR[i][j].phase();
					magnitudeOrPhaseTableG[i][j] = (int)imgG[i][j].phase();
					magnitudeOrPhaseTableB[i][j] = (int)imgB[i][j].phase();
				}
			}
		}
		else if (isReverseFFT)
		{
			for (int i=0; i<magnitudeOrPhaseTableR.length; i++)
			{
				for (int j=0; j<magnitudeOrPhaseTableR[0].length; j++)
				{
					magnitudeOrPhaseTableR[i][j] = (int)imgR[i][j].getReal();
					magnitudeOrPhaseTableG[i][j] = (int)imgG[i][j].getReal();
					magnitudeOrPhaseTableB[i][j] = (int)imgB[i][j].getReal();
				}
			}
		}
		
		
		
		
		
		List<UnprocessedColor> upc = new ArrayList<UnprocessedColor>();
		
		
			for (int i=0; i<imageHeight; i++)
			{
				for (int j=0; j<imageWidth; j++)
				{
					upc.add(new UnprocessedColor(magnitudeOrPhaseTableR[i][j], 
							magnitudeOrPhaseTableG[i][j], magnitudeOrPhaseTableB[i][j]));
				}
			}


		
		if (!isReverseFFT)
		{
			for (int i=0; i<upc.size(); i++)
			{
				upc.get(i).setRed((int)(0.7 * Math.log(1 + upc.get(i).getRed())));
				upc.get(i).setGreen((int)(0.7 * Math.log(1 + upc.get(i).getGreen())));
				upc.get(i).setBlue((int)(0.7 * Math.log(1 + upc.get(i).getBlue())));
			}
		}


		
		Normalization n = new Normalization(upc);
		image = n.normalize(image,0, false);


		
		
		
		long end = System.nanoTime();
		System.out.println((end-start)/1000000000.0);
		
		return image;
	}
	

	
	private ComplexPoint[] countingFHT2(ComplexPoint[] tab, boolean isReverseFFT)
	{
		
		int nbOfLevels = (int)(Math.log(imageHeight)/Math.log(2));

		int[] order = correctOrder(tab.length);
		ComplexPoint[] ordered = new ComplexPoint[order.length];

		for (int i=0; i<tab.length; i++)
		{
			ordered[i] = tab[order[i]];
		}
		
		for (int i=0; i<tab.length; i++)
		{
			tab[i] = ordered[i];
		}
		
		for (int i=0; i<nbOfLevels; i++)
		{
			int nested = (int)Math.pow(2,nbOfLevels-1-i);

			for (int j=0; j<nested; j++)
			{		

				ComplexPoint[] temp = new ComplexPoint[tab.length/nested];


			
				int firstIndex = j *(tab.length/nested);
				int counter = 0;
				
				
				for (int k = firstIndex; k<firstIndex + tab.length/nested; k++)
				{
					temp[counter++] = tab[k];
				}


				ComplexPoint[] result = new ComplexPoint[tab.length];
				
				if (!isReverseFFT)
				{
					result = nextLevel2(temp, tab.length, nested/2, temp.length, 1,j,nested, nbOfLevels,i);
				}
				else
				{
					result = nextLevel2(temp, tab.length, nested/2, temp.length, 1,j,nested, nbOfLevels,i);
				}

				counter = 0;


				for (int k = firstIndex; k<firstIndex + tab.length/nested; k++)
				{
					tab[k] = result[counter++];
				}
				
			}
			
		}

	
		return tab;
	}
	
	
	private ComplexPoint[] nextLevel2(ComplexPoint[] color, int n, int k, int nOfActualTab, int factor, int j, int nested, int nb, int nnn)
	{
		
		int colorLength = color.length;


		ComplexPoint[] clr = new ComplexPoint[colorLength];
		
		int middle = colorLength/2;

		for (int i=0; i<middle; i++)
		{

			clr[i] = new ComplexPoint(color[i].getReal() + color[i+middle].getReal(),0);

			
			clr[i+middle] = new ComplexPoint(color[i].getReal() - color[i+middle].getReal(),0);
		}
	
		if ((j % 2 == 1) && nOfActualTab >= 4 && color.length != n) 
		{
	
					ComplexPoint[] temp = new ComplexPoint[colorLength];
		
					for (int z=0; z<temp.length; z++)
					{
						temp[z] = clr[z];
					}
					
					temp = nestedLevelFHT(temp,k,n,nested,nb,nnn);
					
					for (int z=0; z<temp.length; z++)
					{
						clr[z] = temp[z];
					}
	
		}
		
		
		
		return clr;
	}
	
	
	private ComplexPoint[] nestedLevelFHT(ComplexPoint[] color, double k, int n, int nested, int nb, int j)
	{
		ComplexPoint[] destination = new ComplexPoint[color.length];
		int middle = destination.length/2;


		
		
		for (int i=1; i<middle; i++)
		{
			double arg = 2 *Math.PI * k*i / n;
			
			double cnk = Math.cos(arg) ;
			double snk = Math.sin(arg);
			destination[i] = new ComplexPoint(color[i].getReal() * cnk + color[color.length-i].getReal() * snk,0);

			destination[color.length-i] = new ComplexPoint(color[i].getReal() * snk 
					- color[color.length-i].getReal() * cnk,0);
		}
		destination[0] = color[0];
		destination[middle] = color[middle];
		
		
		return destination;
	}
	
	

	
	
	private static int[] correctOrder(int n)
	{
		int[] order = new int[n];
		
		int nested = (int)(Math.log(n)/Math.log(2));
		order[1] = (int)Math.pow(2,nested-1);
		int temp = 2;
		for (int i=1; i<nested-1; i++)
		{
			
			int index = (int)Math.pow(2,i+1);

			for (int k=temp; k<index; k++)
			{ 	
					
				order[k] = order[k-temp] + (int)Math.pow(2,nested-1-i);
					
			}
			temp = index;

			
			
		}
		
		for (int i=n/2; i<n; i=i+1)
		{
			order[i] = order[i-n/2]+1;
		}

		

		
		return order;
	}
	
	
	
	
	
	

	
	

	
	
	private void writeToTable(BufferedImage image, ComplexPoint[][] imgR,
			ComplexPoint[][] imgG, ComplexPoint[][] imgB)
	{
		int size = imgR.length;
		
		for (int i=0; i<size; i++)
		{
			for (int j=0; j<size; j++)
			{
				int rgb = image.getRGB(j, i);
				int r = (rgb >> 16) & 0xff;
				int g = (rgb >> 8) & 0xff;
				int b = (rgb >> 0) & 0xff;
				imgR[i][j] = new ComplexPoint((double)r, 0.0);
				imgG[i][j] = new ComplexPoint((double)g, 0.0);
				imgB[i][j] = new ComplexPoint((double)b, 0.0);
			}
		}
		
	}
	

	
	private void writeToTableReverse(BufferedImage image, ComplexImage complexImage,
			ComplexPoint[][] imgR, ComplexPoint[][] imgG, ComplexPoint[][] imgB)
	{
		int size = imgR.length;
		
		
		for (int i=0; i<size; i++)
		{
			for (int j=0; j<size; j++)
			{
				imgR[i][j] = new ComplexPoint(complexImage.getPointsR()[i][j].getReal(), complexImage.getPointsR()[i][j].getImaginary());
				imgG[i][j] = new ComplexPoint(complexImage.getPointsG()[i][j].getReal(), complexImage.getPointsG()[i][j].getImaginary());
				imgB[i][j] = new ComplexPoint(complexImage.getPointsB()[i][j].getReal(), complexImage.getPointsB()[i][j].getImaginary());
			}
		}
		
	}
	

	
	public BufferedImage highPassFilter(BufferedImage image)
	{
		return image;
	}

	public Picture getPicture() {
		return picture;
	}

	public boolean isFFT() {
		return isFFT;
	}

	public void setFFT(boolean isFFT) {
		this.isFFT = isFFT;
	}
	
	
	

		
		
	

}
